/**
 * Copyright (C) 2008+ Saifeng Zeng
 *		 2008+ Spark Zheng
 *
 * @file	include/mempool.h
 * @brief 
 *      A thread-safe memory-pool module. A generate infrastructure.
 *      -1. mpool (slab implementation)
 *      -2. marena
 *      -3. naive allocator (simple implementation)
 *
 * @history
 *      version 0.0.1 (initialized by Spark Zheng, use arena as mpool)
 *      version 0.1.0 (enhanced by Saifeng Zeng, add really mpool module)
 *      version 0.2.0 (modified by Spark Zheng, accomplish mpool)
 *      version 0.2.2 (modified by Spark Zheng,
 *	      join arena and mpool in the module)
 *      version 0.3.0 (modified by Saifeng Zeng, change free algorithm,
 *	      abandon rbtree)
 *      version 0.3.1 (modified by Spark Zheng,
 *	      bug-fixed in dlib_mpool_realloc() function.)
 *      version 0.4.0 (modified by Spark Zheng,
 *	      tidy up as generate-utils moudel)
 *	vserion 0.4.2 (add naive allocator)
 *
 */

#ifndef _DLIB_MEMPOOL_H
#define _DLIB_MEMPOOL_H

#ifdef __cplusplus
extern "C" {
#endif

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "global.h"

#ifndef MEM_POISON
#define MEM_POISON	NULL
#endif

/**
 * Basic memory operation interface
 * @warning basic memory operation maybe sleep, use carefully,
 *  or you need dmalloc_nb(nonblock version, need check returns)
 */

/**
 * dmalloc
 * @brief Dmalloc may block when been invoked, don't hold a lock when invoke it.
 * @param size: allocate size
 */
static inline void *dmalloc(uint32_t size)
{
	void *m = NULL;

	while ((m = calloc(1, size)) == NULL) {
		usleep(1);
	}

	return m;
}

/** 
 * dmalloc_nb
 * @brief Dmalloc nonblock version but would return NULL.
 * @param size: allocate size
 */
static inline void *dmalloc_nb(uint32_t size)
{
	void *m;

	if ((m = calloc(1, size)) == NULL) {
		return NULL;
	}

	return m;
}

/** 
 * drealloc
 * @brief will block, like realloc.
 * @param m: same like realloc
 * @param size: same like realloc
 */
static inline void *drealloc(void *m, uint32_t size)
{
	void *tmp;

	while ((tmp = realloc(m, size)) == NULL) {
		usleep(1);
	}
	m = tmp;

	return m;
}

/** 
 * drealloc_nb
 * @brief drealloc nonblock version would return NULL.
 * @param m: same like realloc
 * @param size: same like realloc
 */
static inline void *drealloc_nb(void *m, uint32_t size)
{
	void *tmp;

	if ((tmp = realloc(m, size)) == NULL) {
		return NULL;
	}
	m = tmp;

	return m;
}

/** 
 * dfree
 * @brief Free dirty-heaps
 * @param m: dirty-heaps, not NULL
 */
static inline void dfree(void *m)
{
	if (m != NULL) {
		free(m);
		m = MEM_POISON;
	}

	return;
}

/**
 * A simple allocator module, it has a free heap for allocate memory.
 * the allcator only allocate memory but never free it until it be destroyed.
 * So it is for temp quickly usage, without destruction.
 * from YAEZ-server-framework by Magic Zhao
 * @see naive_alloc_init
 */
typedef struct naive_alloc	dlib_alloc_t;

struct naive_alloc {
	size_t	na_size;	/** total size of the free heap */
	size_t	na_curr;	/** curr head position of the free heap */
	char	na_buf[0];	/** free heap */
};

/**
 * dlib_alloc_init
 * @brief init naive allocator, and set free buffer that can be allocated
 * @return inited allocator object, NULL if buf_len too small
 * @param buf free buffer set to allocator
 * @param buf_len free buffer size
 */
static inline dlib_alloc_t *dlib_alloc_init(char *buf, size_t buf_len)
{
	dlib_alloc_t *na = NULL;

	if (buf_len > sizeof(dlib_alloc_t)) {
		na = (dlib_alloc_t *)buf;
		na->na_curr = 0;
		na->na_size = buf_len - sizeof(dlib_alloc_t);
	}

	return na;
}

/**
 * dlib_alloc_expand
 * @brief expand naive allocator
 * @return return the expand allocator
 * @param na: the original allocator
 * @param buf free buffer set to allocator
 * @param buf_len free buffer size
 */
static inline dlib_alloc_t *dlib_alloc_expand(dlib_alloc_t *na, char *buf, size_t buf_len)
{
	size_t orig_size = na->na_size + sizeof(dlib_alloc_t);

	if (buf_len <= orig_size) {
		return NULL;
	}

	memmove(buf, na, orig_size);
	na = (dlib_alloc_t *)buf;

	return na;
}

/**
 * dlib_naive_alloc
 * @brief get buffer from free buffer set to allocator
 * @return the pointer to new allocated buffer, if no enough space NULL is returned
 * @param na allocator object
 * @param len bytes to be allocated
 */
static inline void *dlib_naive_alloc(dlib_alloc_t *na, size_t len)
{
	if ((na->na_size - na->na_curr) > len) {
		na->na_curr += len;
		return &(na->na_buf[na->na_curr - len]);
	}

	return NULL;
}

/**
 * MPOOL module, the first implementation of mempool
 * @brief memory pool, orginaze chunk fragments in hashtable, provide an interface
 *      like general memory apis.
 *      APIs:
 *      -1. dlib_mpool_init
 *      -2. dlib_mpool_malloc
 */

typedef struct mnode dlib_mnode_t;
typedef struct mhead dlib_mhead_t;
typedef struct mpool dlib_mpool_t;

#ifdef _DEBUG
typedef struct mhstatics dlib_mhstatics_t;
#endif

/**
 * Memory pool allocator interface, and other operations, thread-safe.
 * Warning, all mp operations would be blockable.
 * If need nonblock operations just use dmalloc_nb or
 * modify these mp operations into nonblock version.
 */
#ifdef _DLIB_MPOOL_USE_FACTOR
/**
 * dlib_mpool_init
 * @brief mpool initial function.
 * @param min: first block size of mpool
 * @param max: max block size of mpool, even bigger would not hock on this mpool
 * @param fact: factor between min and max
 */
extern dlib_mpool_t *dlib_mpool_init(uint32_t min, uint32_t max, float fact);
#else
/** 
 * dlib_mpool_init
 * @brief mpool initial function.
 * @param min: first block size of mpool
 * @param max: max block size of mpool, even bigger would not hock on this mpool
 * @param addition: increment between min and max
 */
extern dlib_mpool_t *dlib_mpool_init(uint32_t min, uint32_t max, uint32_t addition);
#endif

/** 
 * dlib_mpool_malloc
 * @brief just like malloc, thread-safe.
 * @param mp: which mpool
 * @param size: chunk size which is needed
 */
extern void *dlib_mpool_malloc(dlib_mpool_t *mp, uint32_t size);

/** 
 * dlib_mpool_calloc
 * @brief just like calloc, thread-safe.
 * @param mp: which mpool
 * @param num: elements number
 * @param size: element size
 */
extern void *dlib_mpool_calloc(dlib_mpool_t *mp, uint32_t num, uint32_t size);

/** 
 * dlib_mpool_realloc
 * @brief just like realloc, thread-safe.
 * @param mp: which mpool
 * @param chunk: the original chunk
 * @param size: chunk size which is needed
 */
extern void *dlib_mpool_realloc(dlib_mpool_t *mp, void *chunk, uint32_t size);

/** 
 * dlib_mpool_free
 * @brief just like free, thread-safe.
 * @param mp: which mpool
 * @param chunk: the chunk need to free
 */
extern void dlib_mpool_free(dlib_mpool_t *mp, void *chunk);

/** 
 * dlib_mpool_tidyup
 * @brief tidyup the unused nodes, run it periodicly.
 * @param mp: which mpool
 * @param mh: which head. all heads when mh == NULL
 */
extern int dlib_mpool_tidyup(dlib_mpool_t *mp, dlib_mhead_t *mh);

/** 
 * dlib_mpool_exit
 * @brief mpool destructure.
 * @param mp: which mpool
 */
extern void dlib_mpool_exit(dlib_mpool_t *mp);

#ifdef _DEBUG
/**
 * dlib_mpool_dump
 * @brief dump the statics-data.
 * @param mp: which mpool
 * @param fd: flush fd, NULL means stdout
 */
extern void dlib_mpool_dump(dlib_mpool_t *mp, FILE *fd);
#endif

/**
 * MARENA(Not-Thread-Safe) module, the second implementation of mempool
 * memory Arena, alloc a big chunk of memory at once,
 *	when need a block, get from marena,
 *	when finished using, don't need do anything,
 *	because MARENA could release it when module destroys.
 * So This is suitable for instant NEED for many memory blocks,
 *	like conf-module.
 * Conplexer than naive allocator.
 * APIs:
 * -1. dlib_marena_new
 * -2. dlib_marena_new_with_heap
 * -3. dlib_marena_calloc
 * -4. dlib_marena_free
 * -5. dlib_marena_tidyup
 * -6. dlib_marena_size
 */

typedef struct maheap dlib_maheap_t;
typedef struct mafree dlib_mafree_t;
typedef struct marena dlib_marena_t;

/**
 * Memory Arena allocator, 
 * Warning, all ma operations would be blockable, not thread-safe.
 * If need nonblock operations just use dmalloc_nb and
 *	modify these ma operations into nonblock version.
 */

/**
 * dlib_marena_new
 * @brief Create a memory arena marked as mp_id(union with mpool)
 */
extern dlib_marena_t *dlib_marena_new(void);

/** 
 * dlib_marena_new_with_heap
 * @brief Create a memory arena marked as mp_id and create a chunk attached to marena. 
 * @param size: memory chunk size
 */
extern dlib_marena_t *dlib_marena_new_with_heap(uint32_t size);

/** 
 * dlib_marena_calloc
 * @brief Calloc a chunk from marena
 * @param ma: marena
 * @param size: memory chunk size
 */
extern void *dlib_marena_calloc(dlib_marena_t *ma, uint32_t size);

/** 
 * dlib_marena_free
 * @brief Free all things in marena
 * @param ma: marena
 */
extern void dlib_marena_free(dlib_marena_t *ma);

/** 
 * dlib_marena_tidyup
 * @brief Tidy up the useness heaps, revert these heaps to system MM, not implement.
 * @param ma: marena
 */
extern void dlib_marena_tidyup(dlib_marena_t *ma);

/** 
 * dlib_marena_size
 * @brief Return the total size of marena
 * @param ma: marena
 */
extern uint32_t dlib_marena_size(dlib_marena_t *ma);

#ifdef __cplusplus
}
#endif
#endif
